package configs

import (
	"github.com/coreos/etcd/client"
	"time"
	"context"
	"sync"
	"github.com/liu-junyong/go-logger/logger"
	"encoding/json"
	"fmt"
	"os"
	"strings"
	"path"
	"../utils"
	"strconv"
)

const (
	LOCAL_BAK_DIR = "etcd_dir/"
	GATE_DIR_KEY  = "/webhook/test/"
)

var ETCD_URL = []string{
	"http://121.43.58.244:2377", "http://121.43.58.244:2378", "http://121.43.58.244:2379",
}

type SubVersionConfig struct {
	Active  bool
	Map_id  []int32
	Name    string
	Size    string
	Url     string
	Version string
}
type VersionConfigs struct {
	cfg map[string]*SubVersionConfig
	mu  sync.Mutex
}

func (this *VersionConfigs) UpdateConfig(key string, valMap map[string]interface{}) {
	this.mu.Lock()
	defer func() {
		this.mu.Unlock()
		if r := recover(); r != nil {
			logger.Error(r)
		}
	}()
	var config *SubVersionConfig
	config, ok := this.cfg[key]
	if !ok {
		config = &SubVersionConfig{}
		this.cfg[key] = config
	}
	for key, value := range valMap {
		switch key {
		case "name":
			config.Name = value.(string)
		case "version":
			config.Version = value.(string)
		case "map":
			config.Map_id = utils.Str2Int32Arr(value.(string))
		case "active":
			b, err := strconv.ParseBool(value.(string))
			if err != nil {
				logger.Error("active传入错误")
				break
			}
			config.Active = b
		case "url":
			config.Url = value.(string)
		case "size":
			config.Size = value.(string)
		}
	}
}

type CardConfig struct {
	CardNum   map[int]int `json:card_num`
	AACardNum map[int]int `json:aa_card_num`
	Free      int         `json:free`
}
type CardConfigs struct {
	cfg map[int32]*CardConfig
	mu  sync.Mutex
}

func (this *CardConfigs) UpdateConfig(key string, valMap map[string]interface{}) {
	this.mu.Lock()
	defer func() {
		this.mu.Unlock()
		if r := recover(); r != nil {
			logger.Error(r)
		}
	}()
	var config *CardConfig
	mapid, err := strconv.Atoi(key)
	if err != nil {
		logger.Error("map id 传入错误", key)
		return
	}
	mapId := int32(mapid)
	config, ok := this.cfg[mapId]
	if !ok {
		config = &CardConfig{}
		this.cfg[mapId] = config
	}
	for key, value := range valMap {
		switch key {
		case "card_num":
			config.CardNum = utils.Str2MapIntInt(value.(string))
		case "aa_card_num":
			config.AACardNum = utils.Str2MapIntInt(value.(string))
		case "free":
			config.Free = value.(int)
		}
	}
	logger.Debug("UpdateConfig ->>> CardConfigs=", config)
	logger.Debug("UpdateConfig ->>> CardConfigs=", this.cfg[mapId].CardNum)
}
func (this *CardConfigs) GetCardUse(mapid int32, AA bool, round int, playercount int) int {
	this.mu.Lock()
	defer func() {
		this.mu.Unlock()
		if r := recover(); r != nil {
			logger.Error(r)
		}
	}()
	config, ok := this.cfg[mapid]
	if ok {
		if AA {
			if ret, res := config.AACardNum[round]; res {
				return ret
			}
		} else {
			if ret, res := config.CardNum[round]; res {
				return ret
			}
		}
	}
	return -1
}

type GateUpdate interface {
	AddUpdateServer(gt *GateNodeConfig)
}

type GateNodeConfig struct {
	Endpoint string  `json:endpoint`
	Map_id   []int32 `json:map_id`
	Status   string  `json:status`
}

type GateConfigs struct {
	cfg map[string]*GateNodeConfig
	mu  sync.Mutex
}

func (this GateConfigs) GetConfigsLength() int {
	this.mu.Lock()
	defer this.mu.Unlock()
	return len(this.cfg)
}
func (this GateConfigs) GetAllConfigs() []*GateNodeConfig {
	this.mu.Lock()
	defer this.mu.Unlock()

	ret := make([]*GateNodeConfig, 0, len(this.cfg))
	for _, value := range this.cfg {
		ret = append(ret, value)
	}
	return ret
}
func (this GateConfigs) UpdateConfig(key string, valMap map[string]interface{}) {
	this.mu.Lock()
	defer func() {
		this.mu.Unlock()
		if r := recover(); r != nil {
			logger.Error(r)
		}
	}()
	var config *GateNodeConfig
	config, ok := this.cfg[key]
	if !ok {
		config = &GateNodeConfig{}
		//bytes, _ := json.Marshal(valMap)
		//json.Unmarshal(bytes, &config)
		this.cfg[key] = config
	}
	for key, value := range valMap {
		switch key {
		case "endpoint":
			config.Endpoint = value.(string)
		case "map_id":
			config.Map_id = utils.Str2Int32Arr(value.(string))
		case "status":
			config.Status = value.(string)
		}
	}
	logger.Debug("UpdateConfig ->>> GateConfig=", *config)

}

type configManager struct {
	tcpManager  GateUpdate
	gate_conf   *GateConfigs
	card_config *CardConfigs
	version     *VersionConfigs
}

var singleton *configManager
var once sync.Once

func GetConfigManger() *configManager {
	once.Do(func() {
		singleton = &configManager{
			gate_conf: &GateConfigs{
				cfg: make(map[string]*GateNodeConfig),
				mu:  sync.Mutex{},
			},
			card_config: &CardConfigs{
				cfg: make(map[int32]*CardConfig),
				mu:  sync.Mutex{},
			},
			version: &VersionConfigs{
				cfg: make(map[string]*SubVersionConfig),
				mu:  sync.Mutex{},
			},
		}
	})
	return singleton
}
func (this *configManager) GetGateConfigs() *GateConfigs {
	return this.gate_conf
}
func (this *configManager) GetCardConfigs() *CardConfigs {
	return this.card_config
}
func (this *configManager) GetVersionConfigs() *VersionConfigs {
	return this.version
}
func (this *configManager) Init(tcpManager GateUpdate) {
	defer func() {
		if r := recover(); r != nil {
			logger.Error(r)
		}
	}()
	this.tcpManager = tcpManager
	this.loadEtcdConfig()
}
func (this *configManager) InitConfig(key string, data map[string]interface{}) {
	switch key {
	case "card_config":
		this.UpdateCardConfig(data)
	case "gate_conf":
		this.InitGateConfig(data)
	case "version":
		this.UpdateVersionConfig(data)
	case "opt_conf":
		this.UpdateOptConfig(data)
	case "prop":
		this.UpdatePropConfig(data)
	case "server_address":
		this.UpdateServerAddConfig(data)
	}
}

//TODO 该节点的子节点变动会刷新整个节点信息，后面可以优化只刷新小节点
func (this *configManager) UpdateConfig(key string, data map[string]interface{}) {
	switch key {
	case "card_config":
		this.UpdateCardConfig(data)
	case "gate_conf":
		this.UpdateGateConfig(data)
	case "version":
		this.UpdateVersionConfig(data)
	case "opt_conf":
		this.UpdateOptConfig(data)
	case "prop":
		this.UpdatePropConfig(data)
	case "server_address":
		this.UpdateServerAddConfig(data)
	}
}
func (this *configManager) UpdateOptConfig(data map[string]interface{}) {
	logger.Info(data)
}
func (this *configManager) UpdatePropConfig(data map[string]interface{}) {
	logger.Info(data)
}
func (this *configManager) UpdateServerAddConfig(data map[string]interface{}) {
	logger.Info(data)
}
func (this *configManager) UpdateCardConfig(data map[string]interface{}) {
	logger.Info(data)
	for key, value := range data {
		this.card_config.UpdateConfig(key, value.(map[string]interface{}))
	}

	logger.Debug("this.card_config -> ", *this.card_config)
}
func (this *configManager) UpdateVersionConfig(data map[string]interface{}) {
	for key, value := range data {
		this.version.UpdateConfig(key, value.(map[string]interface{}))
	}
	//logger.Debug("this.version -> ", *this.version)
	for k, v := range this.version.cfg {
		logger.Info(k, v.Active, v.Size, v.Map_id, v.Name, v.Url, v.Url)
	}
}

func (this *configManager) InitGateConfig(data map[string]interface{}) {
	logger.Debug(data)

	for key, value := range data {
		this.gate_conf.UpdateConfig(key, value.(map[string]interface{}))
	}

	//logger.Debug("this.version -> ", *this.version)
	for k, v := range this.version.cfg {
		logger.Info(k, v.Active, v.Size, v.Map_id, v.Name, v.Url, v.Url)
	}
}

func (this *configManager) UpdateGateConfig(data map[string]interface{}) {
	logger.Debug(data)

	for key, value := range data {
		this.gate_conf.UpdateConfig(key, value.(map[string]interface{}))
		//this.tcpManager.AddUpdateServer(this.gate_conf.cfg[key])
	}

	logger.Debug("this.gate_conf -> ", *this.gate_conf)
}

var etcdWorker Worker

//同步etcd节点数据
func (this *configManager) loadEtcdConfig() {
	//传入数据本地文件夹  etcd节点目录  etcd地址
	etcdWorker = NewWorker(LOCAL_BAK_DIR, GATE_DIR_KEY, ETCD_URL)
	//如果获取etcd 失败,则sleep 60s在获取
	for {
		if etcdWorker.GetRoot() {
			break
		}
		time.Sleep(60 * time.Second)
	}
	go etcdWorker.Watch(GATE_DIR_KEY)
}

type Worker interface {
	Watch(key string)
	GetRoot() bool
}

type work struct {
	detecte_key string
	conf_dir    string
	keysAPI     client.KeysAPI
	mu          sync.Mutex
}

func NewWorker(conf_dir string, detecte_key string, endpoints []string) Worker {
	cfg := client.Config{
		Endpoints:               endpoints,
		Transport:               client.DefaultTransport,
		HeaderTimeoutPerRequest: time.Second,
	}

	etcdClient, err := client.New(cfg)
	if err != nil {
		logger.Error("初始化etcd失败", err)
		return nil
	}

	w := &work{
		detecte_key: detecte_key,
		conf_dir:    conf_dir,
		keysAPI:     client.NewKeysAPI(etcdClient),
		mu:          sync.Mutex{},
	}
	err = os.MkdirAll(conf_dir, 0755)
	if err != nil {
		logger.Error("创建目录失败", conf_dir, err)
	} else {
		logger.Debug("创建目录", conf_dir, "成功")
	}
	return w
}

type TreeNode struct {
	Name     string
	Key      string
	Dir      bool
	Path     string
	Value    string
	Children []*TreeNode
}

func (w *work) GetRoot() bool {
	return w.getRoot(w.detecte_key)
}
func (w *work) getRoot(root string) bool {
	w.mu.Lock()
	defer w.mu.Unlock()

	api := w.keysAPI
	opts := &client.GetOptions{}
	opts.Recursive = true
	resp, err := api.Get(context.Background(), root, opts)
	if err != nil {
		logger.Error("get 数据错误", err)
		return false
	}

	if root == w.detecte_key {
		w.SaveAllNode(resp.Node, root)
	} else {
		w.SaveNode(resp.Node, w.detecte_key)
	}
	return true
}

func (w *work) etcdNodes2Json(node *client.Node, dataMap map[string]interface{}, preKey string) {
	for _, n := range node.Nodes {
		key := strings.Replace(n.Key, preKey+"/", "", 1)
		if n.Nodes != nil {
			tmp := make(map[string]interface{})
			dataMap[key] = tmp
			w.etcdNodes2Json(n, tmp, path.Join(preKey, key))
		} else if !n.Dir {
			dataMap[key] = n.Value
		}
	}
}
func (w *work) SaveNode(n *client.Node, detectKey string) {
	if n.Dir {
		key := n.Key[strings.LastIndex(n.Key, "/")+1:]
		tmp := make(map[string]interface{})
		w.etcdNodes2Json(n, tmp, path.Join(detectKey, key))
		data, _ := json.MarshalIndent(&tmp, "", "   ")
		w.save_key(key, string(data))
		GetConfigManger().UpdateConfig(key, tmp)
	}
}

//程序刚开始运行时,同步 etcd 所有配置文件,存在本地
func (w *work) SaveAllNode(node *client.Node, detectKey string) {
	for _, n := range node.Nodes {
		if n.Dir {
			key := n.Key[strings.LastIndex(n.Key, "/")+1:]
			tmp := make(map[string]interface{})
			w.etcdNodes2Json(n, tmp, path.Join(detectKey, key))
			data, _ := json.MarshalIndent(&tmp, "", "   ")
			w.save_key(key, string(data))

			//初始化内存数据
			logger.Debug("初始化 数据 ", key)
			GetConfigManger().InitConfig(key, tmp)
		}
	}
}
func (w *work) save_key(filename, value string) error {
	f, err := os.Create(path.Join(w.conf_dir, filename+".json"))
	if err != nil {
		return fmt.Errorf("创建文件出现问题了", err)
	}

	_, err = f.WriteString(value)
	if err != nil {
		return fmt.Errorf("文件写入数据失败", err)
	}
	f.Sync()
	return nil
}

func (w *work) Watch(key string) {
	api := w.keysAPI
	watcher := api.Watcher(key, &client.WatcherOptions{
		Recursive: true,
	})
	for {
		res, err := watcher.Next(context.Background())
		if err != nil {
			logger.Error("watch 错误", key, err)
			time.Sleep(time.Second)
			continue
		}

		ress := res
		//go func() {
		logger.Debug("变更：", ress.Node.Key, "   动作 ： ", ress.Action)
		if ress.Action == "set" || ress.Action == "update" {
			if !ress.Node.Dir {
				filename := path.Join(w.conf_dir, ress.Node.Key)
				lastStr := strings.Replace(ress.Node.Key, w.detecte_key, "", 1)
				keys := strings.Split(lastStr, "/")
				filename = keys[0]
				w.getRoot(path.Join(w.detecte_key, filename))
			} else {

			}
		} else if ress.Action == "delete" {
			//TODO 删除节点如何处理

		}
		//}()
	}
}
